package com.lauor.smpedr.core.handler.provider;

import com.lauor.smpedr.core.handler.TypeHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 默认的jdbc 类型与entity 类型转换器
 */
public class DefaultTypeHandler implements TypeHandler {
    private final Logger LOG = LoggerFactory.getLogger(DefaultTypeHandler.class);

    /** 基本数据类型对应方法 */
    private final Map<Class, Method> baseTypeHandlerMap;
    /** 引用数据类型对应方法 */
    private final Map<Class, Method> refTypeHandlerMap;

    public DefaultTypeHandler() {
        baseTypeHandlerMap = new HashMap<>();
        refTypeHandlerMap = new HashMap<>();

        //注册类型理器
        registerBaseTypes();
        registerRefTypes();
    }

    @Override
    public Object jdbcToJavaBeanType(ResultSet resultSet, int columnIndex, Class javaBeanType) throws SQLException {
        Method method = findMethod(javaBeanType);
        if (method != null){
            try {
                Object result = method.invoke(resultSet, columnIndex);
                if (result == null) {
                    return null;
                }
                //判断是否非基本数据类型，并且原值为空的
                if (baseTypeHandlerMap.get(javaBeanType) == null && resultSet.wasNull()){
                    return null;
                }
                return result;
            } catch (IllegalAccessException | InvocationTargetException e) {
                LOG.error(String.format("Invoking type function %s(int columnIndex) error", method.getName()), e);
                return resultSet.getObject(columnIndex);
            }
        }
        return resultSet.getObject(columnIndex);
    }

    private Method findMethod(Class javaBeanType){
        Method method = refTypeHandlerMap.get(javaBeanType);
        return method == null ? baseTypeHandlerMap.get(javaBeanType) : method;
    }

    /** 注册基本数据类型 */
    private void registerBaseTypes() {
        List<Class> typeKeyList = Arrays.asList(long.class, int.class, short.class, byte.class
                , float.class, double.class, boolean.class);

        List<String> functionName = Arrays.asList("getLong", "getInt", "getShort", "getByte"
                , "getFloat", "getDouble", "getBoolean");
        //注册
        Class cls = ResultSet.class;
        for (int i = 0; i < typeKeyList.size(); i++) {
            Method method = null;
            try {
                method = cls.getMethod( functionName.get(i), int.class);
            } catch (NoSuchMethodException ex){
                LOG.error( String.format("Registering types error, method %s(int columnIndex) not found", functionName.get(i)), ex);
            }
            baseTypeHandlerMap.put(typeKeyList.get(i), method);
        }
    }

    /** 注册引用数据类型 */
    private void registerRefTypes() {
        List<Class> typeKeyList = Arrays.asList(Long.class, Integer.class, Short.class, Byte.class
                , Float.class, Double.class, BigDecimal.class
                , Date.class, java.sql.Date.class, Time.class, Timestamp.class
                , String.class, byte[].class, Boolean.class);

        List<String> functionName = Arrays.asList("getLong", "getInt", "getShort", "getByte"
                , "getFloat", "getDouble", "getBigDecimal"
                , "getTimestamp", "getDate", "getTime", "getTimestamp"
                , "getString", "getBytes", "getBoolean");
        //注册
        Class cls = ResultSet.class;
        for (int i = 0; i < typeKeyList.size(); i++) {
            Method method = null;
            try {
                method = cls.getMethod( functionName.get(i), int.class);
            } catch (NoSuchMethodException ex){
                LOG.error( String.format("Registering types error, method %s(int columnIndex) not found", functionName.get(i)), ex);
            }
            refTypeHandlerMap.put(typeKeyList.get(i), method);
        }
    }
}